/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.io;


import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * 从字符输入流中读取文本，缓冲字符以提供对字符、数组和行的有效读取。
 *
 * <p> 可以指定缓冲区大小，也可以使用默认大小。默认值对于大多数目的来说已经足够大了。
 *
 * <p> 一般来说，读取器发出的每个读取请求都会导致对底层字符或字节流发出相应的读取请求。
 * 因此，建议在任何read()操作开销较大的阅读器
 * (如FileReaders和InputStreamReaders周围包装一个BufferedReader。例如,
 *
 * <pre>
 * BufferedReader in
 *   = new BufferedReader(new FileReader("foo.in"));
 * </pre>
 *
 * 将缓冲指定文件的输入。
 * 如果没有缓冲，read()或readLine()的每次调用都可能导致从文件中读取字节，
 * 转换为字符，然后返回，这可能非常低效。
 *
 * <p> 使用DataInputStreams进行文本输入的程序可以
 * 通过使用适当的BufferedReader替换每个DataInputStream进行本地化。
 *
 * @see FileReader
 * @see InputStreamReader
 * @see java.nio.file.Files#newBufferedReader
 *
 * @author      Mark Reinhold
 * @since       JDK1.1
 */

public class BufferedReader extends Reader {

	// 内部的Reader，类似于装饰者模式
    private Reader in;

    // 缓冲数组，默认大小为8192
    private char cb[];
    // nextChar，cb里下一个读取字符的index
    // nChars，cb里读取的最大限制（最右边界）
    private int nChars, nextChar;

    private static final int INVALIDATED = -2;
    private static final int UNMARKED = -1;
    // 标记的char位置，初始为   未标记，-1
    private int markedChar = UNMARKED;
    private int readAheadLimit = 0; /* 仅仅当markedChar > 0  ，有效*/

    /** 如果下一个字符是换行符，跳过它 
     * <p>注意：有两种换行符 Linux \n  Windows \r\n，
     * CR 对应\r   LF对应\n，
     * 当readLine时，碰到了\r时，就终止，然后设置跳过LF即跳过\n
     * */
    private boolean skipLF = false;

    /** 当标记被设置时，skipLF标志*/
    private boolean markedSkipLF = false;

    private static int defaultCharBufferSize = 8192;
    private static int defaultExpectedLineLength = 80;

    /**
     * 创建使用指定大小的输入缓冲区的缓冲字符输入流。
     *
     * @param  in   A Reader
     * @param  sz   Input-buffer size
     *
     * @exception  IllegalArgumentException  If {@code sz <= 0}
     */
    public BufferedReader(Reader in, int sz) {
    	// 用in作为lock
        super(in);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        // 设置in，缓冲区cb，读取字符和读取范围
        this.in = in;
        cb = new char[sz];
        nextChar = nChars = 0;
    }

    /**
     * 创建使用默认大小的输入缓冲区的缓冲字符输入流。
     *
     * @param  in   A Reader
     */
    public BufferedReader(Reader in) {
        this(in, defaultCharBufferSize);
    }

    /** 检查以确保流没有被关闭 */
    private void ensureOpen() throws IOException {
        if (in == null)
            throw new IOException("Stream closed");
    }

    /**
     * 填充输入缓冲区，考虑mark是否有效。
     */
    private void fill() throws IOException {
    	// dst为缓冲区里最大的有效读取范围，之后读取[dst,length)
        int dst;
        // 如果未标记或者标记不合法，dst为0
        if (markedChar <= UNMARKED) {
            dst = 0;
        } else {
        	// 存在标记现象
        	// delta为下一个读取字符和标记字符之间长度
            int delta = nextChar - markedChar;
            // 如果长度超过限制，标记不合法，设置markedChar不合法，readAheadLimit为0，dst为0
            if (delta >= readAheadLimit) {
                markedChar = INVALIDATED;
                readAheadLimit = 0;
                dst = 0;
            } else {
            	// 长度不超过标记限制
            	// 如果标记限制小于等于缓冲区长度，把cb[markedChar,markedChar+delta]移动到cb[0,delta]，markedChar为0，dst为delta
                if (readAheadLimit <= cb.length) {
                    System.arraycopy(cb, markedChar, cb, 0, delta);
                    markedChar = 0;
                    dst = delta;
                } else {
                	// 标记限制大于缓冲区长度
                	// 分配新缓冲区，长度为标记限制，复制就缓存到新缓存ncb[0,delta]
                	// 设置cb，markedChar为0，dst为delta
                    char ncb[] = new char[readAheadLimit];
                    System.arraycopy(cb, markedChar, ncb, 0, delta);
                    cb = ncb;
                    markedChar = 0;
                    dst = delta;
                }
                // // 长度不超过标记限制，之后下一个读取字符的index和读取范围都是delta
                nextChar = nChars = delta;
            }
        }

        int n;
        do {
        	// 填充cb的[dst,cb.length)
            n = in.read(cb, dst, cb.length - dst);
        } while (n == 0);
        if (n > 0) {
            nChars = dst + n;
            nextChar = dst;
        }
    }

    /**
     * 读取单个字符
     *
     * @return The character read, as an integer in the range
     *         0 to 65535 (<tt>0x00-0xffff</tt>), or -1 if the
     *         end of the stream has been reached
     * @exception  IOException  If an I/O error occurs
     */
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            for (;;) {
            	// 如果缓冲区满了，调用fill方法，如果还是满的，返回-1
                if (nextChar >= nChars) {
                    fill();
                    if (nextChar >= nChars)
                        return -1;
                }
                // 如果跳过换行符，先设置skipLF为false，然后读取下一个字符，如果是\n，nextChar++，继续循环
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                        continue;
                    }
                }
                // 返回nextChar位置的字符
                return cb[nextChar++];
            }
        }
    }

    /**
     * 将字符读入数组的一部分，必要时从底层流读取。
     */
    private int read1(char[] cbuf, int off, int len) throws IOException {
    	// 如果缓冲区溢出，调用fill方法
        if (nextChar >= nChars) {
            /* 如果请求的长度至少和缓冲区一样大，如果没有标记/重置活动，
               如果没有跳过换行，就不要麻烦将字符复制到本地缓冲区中。
               通过这种方式，缓冲流将无害地倾泻而下。*/
            if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
                return in.read(cbuf, off, len);
            }
            fill();
        }
        // 如果还是溢出，返回-1
        if (nextChar >= nChars) return -1;
        // 如果跳过换行符，skipLF为false，校验下一个字符
        if (skipLF) {
            skipLF = false;
            if (cb[nextChar] == '\n') {
                nextChar++;
                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars)
                    return -1;
            }
        }
        // 读取min(len,nChars-nextChar)个字符，复制到cbuf中
        int n = Math.min(len, nChars - nextChar);
        System.arraycopy(cb, nextChar, cbuf, off, n);
        nextChar += n;
        return n;
    }

    /**
     * 将字符读入数组的一部分。
     *
     * <p> 这个方法实现了Reader类对应的read方法的通用契约。
     * 为了方便起见，它尝试通过反复调用底层流的read方法来读取尽可能多的字符。
     * 重复读取将继续，直到下列条件之一满足:<ul>
     *
     *   <li> 已读取指定字符数，
     *
     *   <li> 底层流的read方法返回-1，表示文件结束，或
     *
     *   <li> 底层流的ready方法返回false，表示进一步的输入请求将被阻塞。
     *
     * </ul> 如果底层流上的第一次读取返回-1以表示文件结束，则此方法返回-1。
     * 否则，该方法将返回实际读取的字符数。
     *
     * <p> 这个类的子类鼓励(但不是必须)尝试以相同的方式读取尽可能多的字符。
     *
     * <p> 通常，此方法从流的字符缓冲中获取字符，根据需要从底层流中填充字符。
     * 但是，如果缓冲区是空的，标记无效，并且需要的长度至少与缓冲区一样大，
     * 那么该方法将直接将字符从底层流读入给定的数组。
     * 因此，冗余的BufferedReader将不必复制数据。
     *
     * @param      cbuf  Destination buffer
     * @param      off   Offset at which to start storing characters
     * @param      len   Maximum number of characters to read
     *
     * @return     The number of characters read, or -1 if the end of the
     *             stream has been reached
     *
     * @exception  IOException  If an I/O error occurs
     */
    public int read(char cbuf[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }

            // 第一次读取，如果小于等于0，直接返回
            int n = read1(cbuf, off, len);
            if (n <= 0) return n;
            // 当没有读取完，或者in是ready的，进行循环
            while ((n < len) && in.ready()) {
            	// 不断读取，直到返回值<=0，加上读取的字符数
                int n1 = read1(cbuf, off + n, len - n);
                if (n1 <= 0) break;
                n += n1;
            }
            return n;
        }
    }

    /**
     * 读取一行文本。
     * 一个行被认为是由一个换行符LF('\n')，一个回车符('\r')，
     * 或者后跟一个换行符的回车符中的任何一个终止。
     *
     * @param      ignoreLF  如果为true，下一个\n会被跳过
     *
     * @return     A String containing the contents of the line, not including
     *             any line-termination characters, or null if the end of the
     *             stream has been reached
     *
     * @see        java.io.LineNumberReader#readLine()
     *
     * @exception  IOException  If an I/O error occurs
     */
    String readLine(boolean ignoreLF) throws IOException {
        StringBuffer s = null;
        int startChar;

        synchronized (lock) {
            ensureOpen();
            boolean omitLF = ignoreLF || skipLF;

            // 大循环  缓冲loop
        bufferLoop:
            for (;;) {
            	// 如果缓冲区溢出，调用fill
                if (nextChar >= nChars)
                    fill();
                // 如果还是溢出，返回null或者s.toString()
                if (nextChar >= nChars) { /* EOF */
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
                boolean eol = false;
                char c = 0;
                int i;

                /* 如果需要，跳过\n*/
                if (omitLF && (cb[nextChar] == '\n'))
                    nextChar++;
                skipLF = false;
                omitLF = false;

                // 小循环  字符loop
            charLoop:
            	// 从nextChar到nChars进行循环，查找换行符
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    // 如果c是\n或者\r，eol为true，小循环结束
                    if ((c == '\n') || (c == '\r')) {
                        eol = true;
                        break charLoop;
                    }
                }
                // startChar为nextChar，nextChar为nChars或者i（碰到了换行号）
                startChar = nextChar;
                nextChar = i;

                // 如果找到了换行号
                if (eol) {
                    String str;
                    // 生成总的字符串str
                    if (s == null) {
                        str = new String(cb, startChar, i - startChar);
                    } else {
                        s.append(cb, startChar, i - startChar);
                        str = s.toString();
                    }
                    nextChar++;
                    // 如果碰到\r\n的情况，设置了跳过LF，即跳过\n
                    if (c == '\r') {
                        skipLF = true;
                    }
                    return str;
                }

                if (s == null)
                    s = new StringBuffer(defaultExpectedLineLength);
                // s加入一段char
                s.append(cb, startChar, i - startChar);
            }
        }
    }

    /**
     * 读取一行文本。一个行被认为是由一个换行符('\n')，一个回车符('\r')，或者后跟一个换行符的回车符中的任何一个终止。
     *
     * @return     包含行内容的字符串，但不包含任何行终止字符，如果流已经结束，则为null
     *
     * @exception  IOException  If an I/O error occurs
     *
     * @see java.nio.file.Files#readAllLines
     */
    public String readLine() throws IOException {
        return readLine(false);
    }

    /**
     * 跳过字符
     *
     * @param  n  The number of characters to skip
     *
     * @return    The number of characters actually skipped
     *
     * @exception  IllegalArgumentException  If <code>n</code> is negative.
     * @exception  IOException  If an I/O error occurs
     */
    public long skip(long n) throws IOException {
        if (n < 0L) {
            throw new IllegalArgumentException("skip value is negative");
        }
        synchronized (lock) {
            ensureOpen();
            // r为还要跳过的字符数
            long r = n;
            // 当r大于0时
            while (r > 0) {
            	// 如果缓冲区溢出，调用fill
                if (nextChar >= nChars)
                    fill();
                // 如果还是溢出，退出循环
                if (nextChar >= nChars) /* EOF */
                    break;
                // 跳过\n
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                    }
                }
                // d为缓冲区剩余的字符数
                long d = nChars - nextChar;
                // 缓冲区跳过min(r,d)
                // r小于等于d时，跳出循环
                if (r <= d) {
                    nextChar += r;
                    r = 0;
                    break;
                }
                else {
                    r -= d;
                    nextChar = nChars;
                }
            }
            return n - r;
        }
    }

    /**
     * 告知该流是否已准备好读取。
     * 如果缓冲区不是空的，或者底层字符流已经准备好，则缓冲的字符流已经准备好。
     *
     * @exception  IOException  If an I/O error occurs
     */
    public boolean ready() throws IOException {
        synchronized (lock) {
            ensureOpen();

            /*
             * 如果需要跳过换行，而下一个要读取的字符是换行字符，那么就直接跳过它。
             */
            if (skipLF) {
                /* 注意，当且仅当流上的下一个读取没有阻塞时，in.ready()将返回true。
                 */
                if (nextChar >= nChars && in.ready()) {
                    fill();
                }
                if (nextChar < nChars) {
                    if (cb[nextChar] == '\n')
                        nextChar++;
                    skipLF = false;
                }
            }
            // 如果缓冲区没有溢出，或者，in是ready的，返回true
            return (nextChar < nChars) || in.ready();
        }
    }

    /**
     * 告知此流是否支持mark()操作，它确实支持。
     */
    public boolean markSupported() {
        return true;
    }

    /**
     * 标记流中的当前位置。对reset()的后续调用将尝试将流重新定位到此处。
     *
     * @param readAheadLimit   对在保留mark的同时可以读取的字符数量的限制。
     *                         在读取字符到这个限制之后，重置流的尝试可能会失败。
     *                         如果一个限制值大于输入缓存的大小，则会分配一个新的缓冲区，该缓冲区的大小不小于限制值。
     *                         因此，使用大值时要小心。
     *
     * @exception  IllegalArgumentException  If {@code readAheadLimit < 0}
     * @exception  IOException  If an I/O error occurs
     */
    public void mark(int readAheadLimit) throws IOException {
        if (readAheadLimit < 0) {
            throw new IllegalArgumentException("Read-ahead limit < 0");
        }
        synchronized (lock) {
            ensureOpen();
            // 设置3个相关字段
            this.readAheadLimit = readAheadLimit;
            markedChar = nextChar;
            markedSkipLF = skipLF;
        }
    }

    /**
     * 将流重置为最近的标记。
     *
     * @exception  IOException  If the stream has never been marked,
     *                          or if the mark has been invalidated
     */
    public void reset() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (markedChar < 0)
                throw new IOException((markedChar == INVALIDATED)
                                      ? "Mark invalid"
                                      : "Stream not marked");
            // 设置nextChar和skipLF
            nextChar = markedChar;
            skipLF = markedSkipLF;
        }
    }

    // 关闭in，设置cb为null
    public void close() throws IOException {
        synchronized (lock) {
            if (in == null)
                return;
            try {
                in.close();
            } finally {
                in = null;
                cb = null;
            }
        }
    }

    /**
     * 返回一个流，其中的元素是从BufferedReader读取的行。
     * 流是惰性填充的，读只发生在terminal stream operation。
     *
     * <p> 在终端流操作执行期间，不能对读取器进行操作。
     * 否则，终端流操作的结果是未定义的。
     *
     * <p> 在执行完终端流操作后，不能保证读取器将处于读取下一个字符或行所需的特定位置。
     *
     * <p> 如果在访问底层BufferedReader时抛出一个IOException，它将被包装在一个UncheckedIOException中，
     * 该exception将从导致读取发生的Stream方法中抛出。
     * 如果在一个已关闭的BufferedReader上调用此方法，将返回一个Stream。
     * 在流关闭后，任何需要从BufferedReader读取的操作都将导致抛出UncheckedIOException。
     *
     * @return a {@code Stream<String>} providing the lines of text
     *         described by this {@code BufferedReader}
     *
     * @since 1.8
     */
    public Stream<String> lines() {
        Iterator<String> iter = new Iterator<String>() {
            String nextLine = null;

            @Override
            public boolean hasNext() {
                if (nextLine != null) {
                    return true;
                } else {
                    try {
                    	// 调用readLIne方法，将下一行赋值给nextLine
                        nextLine = readLine();
                        return (nextLine != null);
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                }
            }

            @Override
            public String next() {
            	// 调用hasNext，间接调用readLIne
                if (nextLine != null || hasNext()) {
                    String line = nextLine;
                    nextLine = null;
                    return line;
                } else {
                    throw new NoSuchElementException();
                }
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                iter, Spliterator.ORDERED | Spliterator.NONNULL), false);
    }
}
